import frappe
from frappe import _
from frappe.utils import flt
from erpnext.stock.doctype.stock_entry.stock_entry import StockEntry

def before_save(doc, method):
    """
    在保存物料移动单前，如果是工单入库类型，添加委外费用明细
    """
    # 只处理工单入库类型的物料移动单
    if doc.stock_entry_type == "Manufacture" and doc.work_order:
        add_subcontracting_expenses(doc)

def add_subcontracting_expenses(doc):
    """
    添加委外费用明细到额外费用子表
    """
    # 获取工单信息
    work_order = frappe.get_doc("Work Order", doc.work_order)
    
    # 直接获取工单的委外加工费
    total_subcontracting_fee = flt(work_order.custom_subcontracting_fee)
    
    # 如果有委外加工费，添加到额外费用子表
    if total_subcontracting_fee > 0:
        # 获取公司的默认委外加工费科目
        default_expense_account = get_default_subcontracting_account(doc.company)
        
        if not default_expense_account:
            frappe.msgprint(
                _("Default Subcontract Processing Fee Account not set for company {0}. Subcontracting fee will not be added.").format(doc.company),
                alert=True
            )
            return
        
        # 检查是否已经存在委外费用明细
        subcontracting_expense_exists = False
        for expense in doc.additional_costs:
            if expense.expense_account == default_expense_account:
                # 更新现有的委外费用明细
                expense.amount = total_subcontracting_fee
                expense.description = _("Subcontracting Fee for Work Order {0}").format(doc.work_order)
                subcontracting_expense_exists = True
                break
        
        # 如果不存在，添加新的委外费用明细
        if not subcontracting_expense_exists:
            # 添加委外费用明细
            doc.append("additional_costs", {
                "expense_account": default_expense_account,
                "description": _("Subcontracting Fee for Work Order {0}").format(doc.work_order),
                "amount": total_subcontracting_fee
            })
            
            frappe.msgprint(
                _("Added Subcontracting Fee of {0} for Work Order {1}").format(
                    frappe.format(total_subcontracting_fee, {"fieldtype": "Currency"}),
                    doc.work_order
                ),
                alert=True
            )

def get_default_subcontracting_account(company):
    """
    获取公司的默认委外加工费科目
    """
    # 尝试从公司主数据中获取默认委外加工费科目
    default_account = frappe.db.get_value("Company", company, "custom_default_subcontract_processing_fee_account")
    
    return default_account 

class CustomStockEntry(StockEntry):
    def set_process_loss_qty(self):
        """
        重写设置制程损耗数量方法，考虑已入库的损耗数量
        """
        if self.purpose not in ("Manufacture", "Repack"):
            return

        precision = self.precision("process_loss_qty")
        
        if self.work_order:
            # 获取工单的最后一个工序
            last_operation = frappe.get_all(
                "Work Order Operation",
                filters={"parent": self.work_order},
                fields=["name", "process_loss_qty"],
                order_by="idx desc",
                limit=1
            )
            
            if last_operation and last_operation[0].process_loss_qty is not None:
                total_process_loss_qty = flt(last_operation[0].process_loss_qty, precision)
                
                # 获取已经入库的制程损耗数量
                existing_process_loss = frappe.db.sql("""
                    SELECT SUM(process_loss_qty) 
                    FROM `tabStock Entry`
                    WHERE work_order = %s 
                    AND purpose = 'Manufacture'
                    AND docstatus = 1
                    AND name != %s
                """, (self.work_order, self.name or ""), as_list=1)
                
                existing_process_loss_qty = flt(existing_process_loss[0][0] if existing_process_loss and existing_process_loss[0][0] else 0, precision)
                
                # 计算剩余可用的制程损耗数量
                remaining_process_loss_qty = flt(total_process_loss_qty - existing_process_loss_qty, precision)
                
                # 如果剩余损耗数量小于0，则设为0
                remaining_process_loss_qty = max(0, remaining_process_loss_qty)
                
                # 如果当前设置的损耗数量与计算的剩余损耗数量不同，则更新
                if flt(self.process_loss_qty, precision) != flt(remaining_process_loss_qty, precision):
                    self.process_loss_qty = flt(remaining_process_loss_qty, precision)
                    
                    frappe.msgprint(
                        _("The Process Loss Qty has been set to the remaining available loss qty ({0})").format(
                            remaining_process_loss_qty
                        ), 
                        alert=True
                    )
        
        # 如果没有设置制程损耗百分比和数量，则从BOM中获取
        if not self.process_loss_percentage and not self.process_loss_qty:
            self.process_loss_percentage = frappe.get_cached_value(
                "BOM", self.bom_no, "process_loss_percentage"
            )
        
        # 根据百分比计算数量，或根据数量计算百分比
        if self.process_loss_percentage and not self.process_loss_qty:
            self.process_loss_qty = flt(
                (flt(self.fg_completed_qty) * flt(self.process_loss_percentage)) / 100
            )
        elif self.process_loss_qty and not self.process_loss_percentage:
            self.process_loss_percentage = flt(
                (flt(self.process_loss_qty) / flt(self.fg_completed_qty)) * 100
            ) if self.fg_completed_qty else 0
